
//  Copyright (C) 2009-2011 Luca Piccioni
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
// 
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Diagnostics;

using Derm.OpenGL;

namespace Derm.Render
{
	/// <summary>
	/// Exception thrown from class used for rendering operations.
	/// </summary>
	public class RenderException : Exception
	{
		#region Constructors

		/// <summary>
		/// Construct a RenderException specifying the error code.
		/// </summary>
		/// <param name="err">
		/// A <see cref="System.Int32"/> that specifies the error code (code returned by <see cref="Gl.GetError"/>).
		/// </param>
		public RenderException(int err) : base()
		{
			mErrCode = err;
		}

		/// <summary>
		/// Construct a RenderException specifying the error code and a message.
		/// </summary>
		/// <param name="err">
		/// A <see cref="System.Int32"/> that specifies the error code (code returned by <see cref="Gl.GetError"/>).
		/// </param>
		/// <param name="message">
		/// A <see cref="System.String"/> that specifies an additional message.
		/// </param>
		public RenderException(int err, string message) : base(message)
		{
			mErrCode = err;
		}

		/// <summary>
		/// Construct a RenderException specifying the error code and a message.
		/// </summary>
		/// <param name="err">
		/// A <see cref="System.Int32"/> that specifies the error code (code returned by <see cref="Gl.GetError"/>).
		/// </param>
		/// <param name="format">
		/// A <see cref="System.String"/> that specifies an additional message format string.
		/// </param>
		/// <param name="args">
		/// A <see cref="T:System.Object[]"/> that specifies formatted arguments in <paramref name="format"/>.
		/// </param>
		public RenderException(int err, string format, params object[] args) : base(String.Format(format, args))
		{
			mErrCode = err;
		}

		#endregion

		#region Error checking

		/// <summary>
		/// Check for OpenGL errors.
		/// </summary>
		/// <returns>
		/// It returns true in the case non errors was raised by the previous operations, otherwise
		/// it throws a RenderException.
		/// </returns>
		/// <remarks>
		/// The returned value is necessary for embedding this routine call into an assertion statemenet,
		/// since it must be executed at runtime in only few situations.
		/// </remarks>
		public static bool CheckErrors()
		{
			int err;

			// Question: shall GetError be executed in a loop?

			if ((err = Gl.GetError()) != Gl.NO_ERROR) {
				switch (err) {

				case Gl.INVALID_ENUM:
					throw new RenderException(err, "invalid enumeration");
				case Gl.INVALID_VALUE:
					throw new RenderException(err, "invalid value");
				case Gl.INVALID_OPERATION:
					throw new RenderException(err, "invalid operation");
				case Gl.OUT_OF_MEMORY:
					throw new RenderException(err, "out of memory");
				case Gl.INVALID_FRAMEBUFFER_OPERATION:
					throw new RenderException(err, "invalid framebuffer operation");

				
				case Wgl.ERROR_INVALID_PIXEL_TYPE_ARB:
					throw new RenderException(err, "invalid pixel type");
				case Wgl.ERROR_INVALID_PROFILE_ARB:
					throw new RenderException(err, "invalid profile");
				case Wgl.ERROR_INVALID_VERSION_ARB:
					throw new RenderException(err, "invalid version");

				case Wgl.ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB:
					throw new RenderException(err, "incompatible device contexts");

				default:
					throw new RenderException(err, "unknown error 0x"+err.ToString("X8"));
				}
			}

			return (true);
		}

		/// <summary>
		/// Check for OpenGL errors.
		/// </summary>
		/// <returns>
		/// It returns true in the case non errors was raised by the previous operations, otherwise
		/// it throws a RenderException.
		/// </returns>
		/// <remarks>
		/// The returned value is necessary for embedding this routine call into an assertion statemenet,
		/// since it must be executed at runtime in only few situations.
		/// </remarks>
		[Conditional("DEBUG")]
		public static void DebugCheckErrors()
		{
			CheckErrors();
		}

		#endregion

		#region OpenGL Error Code

		/// <summary>
		/// Exception error code.
		/// </summary>
		public int ErrorCode
		{
			get { return (mErrCode); }
		}

		/// <summary>
		/// Fatal error code.
		/// </summary>
		public bool IsFatal
		{
			get {
				switch (mErrCode) {
				case Gl.OUT_OF_MEMORY:
					return (false);
				default:
					return (true);
				}
			}
		}

		/// <summary>
		/// OpenGL error code.
		/// </summary>
		private int mErrCode;

		#endregion
	}
}
